namespace RL

open Raylib_CsLo
open Garnet.Composition
open RL.Action
open RL.Components
open RL.Drawing
open RL.Mapping
open RL.Pathfinding
open RL.Tiles

module Input =
//
// Control Definitions
//
    let private MovePlayer(delta : Point2D, world : Container, ss : SharedState, cardinalKeys : bool) =
        let delta =
            if cardinalKeys && Raylib.IsKeyDown(KeyboardKey.KEY_LEFT_SHIFT) then
                match delta with
                    | { x = 0; y = -1 } -> Point2D.UpRight
                    | { x = 0; y = 1 }  -> Point2D.DownLeft
                    | { x = -1; y = 0 } -> Point2D.UpLeft
                    | _ -> Point2D.DownRight
            else
                delta

        let result, map : bool * RLMap = world.TryGetResource("Map")            // Try to open the map.
        if result then
            let playerQ = world.Query<Eid, Player>() |> Seq.exactlyOne // Query for the one player entity.
            let player = world.Get(playerQ.Value1)
            let pos = player.Get<Point2D>()
            let dest = pos + delta

            let tile = map.GetTile dest.x dest.y  // Get the tile at the intended player destination.
            match tile.tileType with
                | TileType.Floor ->
                    match GetBlockingEntityAt(world, dest) with
                        // Move the player to the destination if not blocked.
                        | None ->
                            player.Set(ActionDefs.Move(dest))
                            world.Run <| ProcessActions()
                            world.Run <| { PlayerRefresh.s = ss }
                        // Otherwise, the player interacts with the entity.
                        | Some(t) ->
                            match t.Has<Enemy>() with
                                // Attack if we know this entity is an enemy.
                                | true ->
                                    player.Set(ActionDefs.Attack t.Id)
                                    world.Run <| ProcessActions()
                                    world.Run <| { PlayerRefresh.s = ss }
                                | false -> ()
                | _ -> ()

    let private WaitPlayer(world : Container, ss : SharedState) =
        let playerQ = world.Query<Eid, Player>() |> Seq.exactlyOne // Query for the one player entity.
        let player = world.Get(playerQ.Value1)
        player.Set(ActionDefs.ShortWait)
        world.Run <| ProcessActions()
        world.Run <| { PlayerRefresh.s = ss }

    let private Keybinds = [
            (KeyboardKey.KEY_UP,     fun (world, ss) -> MovePlayer(Point2D.Up, world, ss, true));      // Move player up
            (KeyboardKey.KEY_DOWN,   fun (world, ss) -> MovePlayer(Point2D.Down, world, ss, true));    // Move player down
            (KeyboardKey.KEY_LEFT,   fun (world, ss) -> MovePlayer(Point2D.Left, world, ss, true));    // Move player left
            (KeyboardKey.KEY_RIGHT,  fun (world, ss) -> MovePlayer(Point2D.Right, world, ss, true));   // Move player right
            (KeyboardKey.KEY_PERIOD, fun (world, ss) -> WaitPlayer(world, ss));
            (KeyboardKey.KEY_KP_5,   fun (world, ss) -> WaitPlayer(world, ss));
        ]
//
// System Definitions
//
    [<Struct>] type InputTrigger = { s : SharedState }

    type Container with
        member c.RegisterInputHandler =
            c.On<InputTrigger> <| fun r ->
                if Raylib.WindowShouldClose() then r.s.QuitFlag <- true
                let rec nextKey i =
                    let key, fn = Keybinds[i]
                    if Raylib.IsKeyPressed(key) then fn(c, r.s)
                    else if i < Keybinds.Length - 1 then nextKey (i + 1)
                nextKey 0

    let init(c : Container) =
        Disposable.Create[c.RegisterInputHandler] |> ignore
